package com.imooc.api;


import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.spring.annotation.GlobalTransactional;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @version 1.0
 * @Author Mr.Yang
 * @Date 2024-08-27 10:40
 * @question: 使用全局异常捕获后 openFeign的被调用方出现异常不会主动回滚
 *              通过切面的方法进行手动回滚
 */

@Slf4j
@Aspect
@Component
public class SeataTransactionAspect {

    /**
     * 全局异常之前 手动去开启全局异常捕获
     * @param joinPoint
     * @return
     * @throws TransactionException
     */
    @Before("execution(* com.imooc.service.impl..*.*(..))")
    public void beginTransaction(JoinPoint joinPoint) throws TransactionException {

        //去查看方法当中是否开启了 异常或者全局异常
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();

        if (isTransactional(signature.getMethod())) {
            log.info("手动开启全局事务，方法被@GlobalTransactional注解标记，方法名为： {}",signature.getMethod().getName());
            GlobalTransaction gt = GlobalTransactionContext.getCurrentOrCreate();
            gt.begin();
        } else {
            log.info("方法没有被@GlobalTransactional注解，不开启全局事务，方法名为： {}",signature.getMethod().getName());
        }

    }

    /**
     * 开始捕获异常 如有则手动回滚
     * @param throwable
     */
    @AfterThrowing(
            throwing = "throwable",
            pointcut = "execution(* com.imooc.service.impl..*.*(..))")
    public void seataRollback(Throwable throwable) throws TransactionException {

        log.info("捕获到异常信息： " + throwable.getMessage());

        //从当前线程当中去获取xid
        String xid = RootContext.getXID();
        if (StringUtils.isNotBlank(xid)) {
            GlobalTransactionContext.reload(xid).rollback();
        }

    }

    /**
     * 判断是否包含此两个注解
     * @param method
     * @return
     */
    private boolean isTransactional(Method method) {
        return method.isAnnotationPresent(GlobalTransactional.class);
    }

}
